/****************************************************************
 *This file is pxie_dll_lib.c
 ****************************************************************/

//本库使用的头文件
#include "lib_hitmc_pxie_opt.h"

//ioctl操作命令,设置要进行重映射的BAR空间
#define BAR_NUM_SET         0xfee50001

//ioctl操作命令,获取PCI的bus地址
#define PCI_BUS_GET         0xfee50010

//ioctl操作命令,获取厂商ID
#define GET_VENDOR_ID 		0xfee50020

//ioctl操作命令,获取设备ID
#define GET_DEVICE_ID 		0xfee50021

//ioctl操作命令,获取槽位号
#define GET_SLOT_NUM 		0xfee50022

//ioctl操作命令,获取槽位号
#define GET_SLOT_NUM 		0xfee50022

//ioctl操作命令,获取总线号
#define GET_BUS_NUM 		   0xfee50023

//pxi调用函数
int pxie_open(const char *pathname);
int pxie_close(int pxie_fd);
ssize_t pxie_read(int pxie_fd, unsigned int *buf, size_t count);
ssize_t pxie_write(int pxie_fd, const unsigned int *buf, size_t count);
off_t pxie_lseek(int pxie_fd, off_t offset, int whence);
long pxie_ioctl(int pxie_fd, unsigned int cmd, unsigned long *opt_data);
  
// 链表节点结构体  
typedef struct Node {  
    int reg_fd;  
    int h2c_fd;  
    int c2h_fd;  
    struct Node *next;  
} Node;  

Node *head = NULL; // 初始化链表头指针为空
  
// 插入节点到链表  
int insert_list(int reg_fd, int h2c_fd, int c2h_fd, Node **head) {  
    Node *new_node = (Node *)malloc(sizeof(Node));  
    new_node->reg_fd = reg_fd;  
    new_node->h2c_fd = h2c_fd;  
    new_node->c2h_fd = c2h_fd;  
    new_node->next = *head;  
    *head = new_node;  
    return VI_SUCCESS;  
}  
  
// 在链表中查找节点  
int find_list(int reg_fd, int *h2c_fd, int *c2h_fd, Node *head) {  
    Node *current = head;  
    while (current != NULL) {  
        if (current->reg_fd == reg_fd) {  
            *h2c_fd = current->h2c_fd;  
            *c2h_fd = current->c2h_fd;  
            return VI_SUCCESS;  
        }  
        current = current->next;  
    }  
    return -1; // 没找到返回-1  
}  
  
// 从链表中删除节点  
int del_list(int reg_fd, Node **head) {  
    Node *current = *head;  
    Node *prev = NULL;  
    while (current != NULL) {  
        if (current->reg_fd == reg_fd) {  
            if (prev == NULL) {  
                *head = current->next;  
            } else {  
                prev->next = current->next;  
            }  
            free(current);  
            return VI_SUCCESS;  
        }  
        prev = current;  
        current = current->next;  
    }  
    return -1; // 没找到返回-1  
}

/****************************************************************
 *pxie_open 打开FPGA设备函数
 input:
    pathname:设备文件名
 output:
    文件操作符
 ****************************************************************/
int pxie_open(const char *pathname)
{
    int pxie_fd = 0;

    //打开设备文件
    pxie_fd = open(pathname, O_RDWR | O_SYNC);

    //返回文件描述符
    return pxie_fd;
}

/****************************************************************
 *pxie_close 关闭FPGA设备函数
 input:
    pxie_fd:设备文件描述符
 output:
    操作结果
 ****************************************************************/
int pxie_close(int pxie_fd)
{
   int ret = 0;

   //关闭设备
   ret = close(pxie_fd);

   //返回设备关闭情况
   return ret;
}

/****************************************************************
 *pxie_read 读设备操作
 input:
    pxie_fd:设备文件描述符
    buf:存储读取到数据的缓冲区指针
    count:准备读取的数据长度
 output:
    实际读取的设备长度
 ****************************************************************/
ssize_t pxie_read(int pxie_fd, unsigned int *buf, size_t count)
{
   //调用设备读方法，进行设备读
   return read(pxie_fd, (void *)buf, count);
}

/****************************************************************
 *pxie_write 写设备操作
 input:
    pxie_fd:设备文件描述符
    buf:存储写入的数据的缓冲区指针
    count:准备写入的数据长度
 output:
    实际写入的设备长度
 ****************************************************************/
ssize_t pxie_write(int pxie_fd, const unsigned int *buf, size_t count)
{
   //调用设备写方法，进行设备写
   return write(pxie_fd, (void *)buf, count);
}
 	
/****************************************************************
 *pxie_lseek 文件位置定位设备操作
 input:
    pxie_fd:设备文件描述符
    offset:文件位置
    whence:操作模式
 output:
    当前文件的位置
 ****************************************************************/
off_t pxie_lseek(int pxie_fd, off_t offset, int whence)
{
   //调用设备的lseek方法
   return lseek(pxie_fd, offset, whence);
}

/****************************************************************
 *pxie_ioctl ioctl设备操作
 input:
    pxie_fd:设备文件描述符
    cmd:命令码
    opt_data:命令码对应的操作值
 output:
    操作返回值
 ****************************************************************/
long pxie_ioctl(int pxie_fd, unsigned int cmd, unsigned long *opt_data)
{
   return ioctl(pxie_fd, cmd, opt_data);
}

/****************************************************************
 *PXIe_viOpen vi开卡函数
 input:
    sesn：填充空
    name：板卡名称
    mode：板卡模式
    timeout：开卡超时
    vi：开卡后返回句柄
 ViStatus:
    函数操作结果
 ****************************************************************/
ViStatus _VI_FUNC  PXIe_viOpen (ViSession sesn, ViRsrc name, ViAccessMode mode, ViUInt32 timeout, ViPSession vi)
{
   ViStatus ret = 0;
   ViChar pxie_h2c_name[] = {"/dev/slot05_0b_hitmc0_h2c_0"};
   ViChar pxie_c2h_name[] = {"/dev/slot05_0b_hitmc0_c2h_0"};

   int h2c_fd = 0;
   int c2h_fd = 0;

   // 调用开卡函数
   *vi = 0;
   *vi = pxie_open(name);

   // 根据开卡函数，填充函数调用之
   if(*vi <= 0){
      return -1;
   } else {
      ret = VI_SUCCESS;
   }

   // 更改h2c的名称
   pxie_h2c_name[9] = name[9];
   pxie_h2c_name[10] = name[10];
   pxie_h2c_name[12] = name[12];
   pxie_h2c_name[13] = name[13];

   // 更改h2c的名称
   pxie_c2h_name[9] = name[9];
   pxie_c2h_name[10] = name[10];
   pxie_c2h_name[12] = name[12];
   pxie_c2h_name[13] = name[13];

   // 调用h2c的DMA文件
   h2c_fd = 0;
   h2c_fd = pxie_open(pxie_h2c_name);

   // 根据开卡函数，填充函数调用之
   if(h2c_fd<= 0){
      return -1;
   } else {
      ret = VI_SUCCESS;
   }

   // 调用c2h的DMA文件
   c2h_fd = 0;
   c2h_fd = pxie_open(pxie_c2h_name);

   // 根据开卡函数，填充函数调用之
   if(c2h_fd<= 0){
      return -1;
   } else {
      ret = VI_SUCCESS;
   }

   // 插入节点到链表  
   ret = insert_list(*vi, h2c_fd, c2h_fd, &head);  
   
   return ret;
}

/****************************************************************
 *PXIe_viClose vi关卡
 input:
    sesn：填充空
    name：板卡名称
    mode：板卡模式
    timeout：开卡超时
    vi：开卡后返回句柄
 ViStatus:
    函数操作结果
 ****************************************************************/
ViStatus _VI_FUNC  PXIe_viClose(ViObject vi)
{
   ViStatus ret = 0;
   int h2c_fd = 0;
   int c2h_fd = 0;

   // 查找h2c和c2h的文件描述符
   ret = find_list(vi, &h2c_fd, &c2h_fd, head);

   // 调用关卡函数
   ret = pxie_close(vi);
   ret = pxie_close(h2c_fd);
   ret = pxie_close(c2h_fd);

   // 删除该节点
   ret = del_list(vi, &head);

   return ret;
}

/****************************************************************
 *PXIe_viDevmsg 读取板卡信息
 input:
    vi：句柄
    vendor_id：厂商编号
    pci_device_id：仪器编号
    pci_slot_id：slot号
    pci_bus_id：总线号
 ViStatus:
    函数操作结果
 ****************************************************************/
ViStatus _VI_FUNC  PXIe_viDevmsg (ViSession vi, ViPUInt16 vendor_id, ViPUInt16 pci_device_id, ViPUInt16 pci_slot_id, ViPUInt16 pci_bus_id)
{
   // 获取板卡ID，并打印
   *vendor_id = pxie_ioctl(vi, GET_VENDOR_ID, NULL);
   *pci_device_id = pxie_ioctl(vi, GET_DEVICE_ID, NULL);
   *pci_slot_id = pxie_ioctl(vi, GET_SLOT_NUM, NULL);
   *pci_bus_id = pxie_ioctl(vi, GET_BUS_NUM, NULL);

   return VI_SUCCESS;
}

/****************************************************************
 *PXIe_viIn32 32位读取寄存器
 input:
    vi：句柄
    space：bar空间，0,1,2,3,4,5
    offset：偏移量
    val32：读取返回值
 ViStatus:
    函数操作结果
 ****************************************************************/
ViStatus _VI_FUNC  PXIe_viIn32 (ViSession vi, ViUInt16 space, ViBusAddress offset, ViPUInt32 val32)
{
   // 设置操作寄存器的位置
   lseek(vi, offset, SEEK_SET);

   // 读取数据
   read(vi, val32, 1);  

   return VI_SUCCESS;
}

/****************************************************************
 *PXIe_viIn32 32位写入寄存器
 input:
    vi：句柄
    space：bar空间，0,1,2,3,4,5
    offset：偏移量
    val32：写入数据
 ViStatus:
    函数操作结果
 ****************************************************************/
ViStatus _VI_FUNC  PXIe_viOut32 (ViSession vi, ViUInt16 space, ViBusAddress offset, ViUInt32  val32)
{
   // 设置操作寄存器的位置
   lseek(vi, offset, SEEK_SET);

   // 读取数据
   write(vi, &val32, 1);  

   return VI_SUCCESS;
}

/****************************************************************
 *PXIe_viIn32_dma 对同一地址进行32位DMA读
 input:
    vi：句柄
    space：bar空间，0,1,2,3,4,5
    offset：偏移量
    val32：存储读取序列的值
    val32_len：读取的个数
 ViStatus:
    函数操作结果
 ****************************************************************/
ViStatus _VI_FUNC  PXIe_viIn32_dma (ViSession vi, ViUInt16 space, ViBusAddress offset, ViPUInt32 val32, ViUInt32 val32_len)
{
   ViStatus ret = VI_SUCCESS;
   int h2c_fd = 0;
   int c2h_fd = 0;
   int rd_ret = 0;

   // 查找h2c和c2h的文件描述符
   ret = find_list(vi, &h2c_fd, &c2h_fd, head);

   // 防止编译警告
   h2c_fd = h2c_fd;
   c2h_fd = c2h_fd;

   // 指定起始地址
   lseek(c2h_fd, (off_t)offset, SEEK_SET);

   // 读取数据
   rd_ret = read(c2h_fd, val32, val32_len);

   if(rd_ret <= 0){
      return -1;
   } else if ((ViUInt32)rd_ret != val32_len){
      return -1;
   } else {
      ;
   }

   return ret;
}

/****************************************************************
 *PXIe_viIn32 对同一地址进行32位DMA写
 input:
    vi：句柄
    space：bar空间，0,1,2,3,4,5
    offset：偏移量
    val32：存储写入序列的值
    val32_len：写入的个数
 ViStatus:
    函数操作结果
 ****************************************************************/
ViStatus _VI_FUNC  PXIe_viOut32_dma (ViSession vi, ViUInt16 space, ViBusAddress offset, ViPUInt32  val32, ViUInt32 val32_len)
{
   ViStatus ret = VI_SUCCESS;
   int h2c_fd = 0;
   int c2h_fd = 0;
   int wr_ret = 0;

   // 查找h2c和c2h的文件描述符
   ret = find_list(vi, &h2c_fd, &c2h_fd, head);

   // 防止编译警告
   h2c_fd = h2c_fd;
   c2h_fd = c2h_fd;

   // 指定起始地址
   lseek(h2c_fd, (off_t)offset, SEEK_SET);

   // 写入数据
   wr_ret = write(h2c_fd, val32, val32_len);
   if(wr_ret <= 0){
      return -1;
   } else if ((ViUInt32)wr_ret != val32_len){
      return -1;
   } else {
      ;
   }

   return ret;
}


